package com.ciccwm.core.util;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * 加密解密工具类.
 * 提供常用的加密解密方法.
 *
 * @author ciccwm
 */
public final class EncryptUtils {
    private static final String MD5_ALGORITHM = "MD5";
    private static final String SHA256_ALGORITHM = "SHA-256";
    private static final String AES_ALGORITHM = "AES";
    private static final String RSA_ALGORITHM = "RSA";
    private static final String RSA_SIGNATURE_ALGORITHM = "SHA256withRSA";
    private static final int RSA_KEY_SIZE = 2048;
    private static final int AES_KEY_SIZE = 256;
    private static final int SALT_LENGTH = 16;

    private EncryptUtils() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    /**
     * 密码加密.
     * 使用随机盐值对密码进行加密，返回格式为：盐值$加密后的密码.
     *
     * @param password 原始密码
     * @return 加密后的密码（包含盐值）
     */
    public static String encryptPassword(final String password) {
        if (password == null) {
            return null;
        }
        try {
            // 生成随机盐值
            byte[] salt = generateSalt();
            // 使用盐值加密密码
            String hashedPassword = hashPassword(password, salt);
            // 将盐值和加密后的密码组合
            return Base64.getEncoder().encodeToString(salt) + "$" + hashedPassword;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("密码加密失败", e);
        }
    }

    /**
     * 验证密码.
     *
     * @param password 原始密码
     * @param encryptedPassword 加密后的密码（包含盐值）
     * @return 是否匹配
     */
    public static boolean verifyPassword(final String password, final String encryptedPassword) {
        if (password == null || encryptedPassword == null) {
            return false;
        }
        String[] parts = encryptedPassword.split("\\$");
        if (parts.length != 2) {
            return false;
        }
        try {
            byte[] salt = Base64.getDecoder().decode(parts[0]);
            String hashedPassword = hashPassword(password, salt);
            // 比较两个加密后的密码
            return hashedPassword.equals(parts[1]);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("密码验证失败", e);
        }
    }

    /**
     * 生成随机盐值.
     *
     * @return 盐值字节数组
     */
    public static byte[] generateSalt() {
        byte[] salt = new byte[SALT_LENGTH];
        new SecureRandom().nextBytes(salt);
        return salt;
    }

    /**
     * 使用盐值对密码进行加密.
     *
     * @param password 原始密码
     * @param salt 盐值
     * @return 加密后的密码
     * @throws NoSuchAlgorithmException 如果加密算法不可用
     */
    public static String hashPassword(final String password, final byte[] salt) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance(SHA256_ALGORITHM);
        md.update(salt);
        byte[] bytes = md.digest(password.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(bytes);
    }

    /**
     * MD5加密.
     *
     * @param text 待加密文本
     * @return 加密后的文本
     */
    public static String md5(final String text) {
        if (text == null) {
            return null;
        }
        try {
            MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM);
            byte[] bytes = md.digest(text.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(bytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }

    /**
     * SHA-256加密.
     *
     * @param text 待加密文本
     * @return 加密后的文本
     */
    public static String sha256(final String text) {
        if (text == null) {
            return null;
        }
        try {
            MessageDigest md = MessageDigest.getInstance(SHA256_ALGORITHM);
            byte[] bytes = md.digest(text.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(bytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256加密失败", e);
        }
    }

    /**
     * AES加密.
     *
     * @param text 待加密文本
     * @param key 密钥
     * @return 加密后的文本
     */
    public static String aesEncrypt(final String text, final String key) {
        if (text == null || key == null) {
            return null;
        }
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
            Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] bytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(bytes);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("AES加密失败", e);
        }
    }

    /**
     * AES解密.
     *
     * @param text 待解密文本
     * @param key 密钥
     * @return 解密后的文本
     */
    public static String aesDecrypt(final String text, final String key) {
        if (text == null || key == null) {
            return null;
        }
        try {
            SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM);
            Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(text));
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("AES解密失败", e);
        }
    }

    /**
     * 生成RSA密钥对.
     *
     * @return RSA密钥对
     */
    public static KeyPair generateRsaKeyPair() {
        try {
            KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
            generator.initialize(RSA_KEY_SIZE, new SecureRandom());
            return generator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("生成RSA密钥对失败", e);
        }
    }

    /**
     * RSA公钥加密.
     *
     * @param text 待加密文本
     * @param publicKey 公钥
     * @return 加密后的文本
     */
    public static String rsaEncrypt(final String text, final String publicKey) {
        if (text == null || publicKey == null) {
            return null;
        }
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey key = keyFactory.generatePublic(keySpec);
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] bytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(bytes);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("RSA加密失败", e);
        }
    }

    /**
     * RSA私钥解密.
     *
     * @param text 待解密文本
     * @param privateKey 私钥
     * @return 解密后的文本
     */
    public static String rsaDecrypt(final String text, final String privateKey) {
        if (text == null || privateKey == null) {
            return null;
        }
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey key = keyFactory.generatePrivate(keySpec);
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(text));
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException
                | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("RSA解密失败", e);
        }
    }

    /**
     * RSA私钥签名.
     *
     * @param text 待签名文本
     * @param privateKey 私钥
     * @return 签名后的文本
     */
    public static String rsaSign(final String text, final String privateKey) {
        if (text == null || privateKey == null) {
            return null;
        }
        try {
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PrivateKey key = keyFactory.generatePrivate(keySpec);
            Signature signature = Signature.getInstance(RSA_SIGNATURE_ALGORITHM);
            signature.initSign(key);
            signature.update(text.getBytes(StandardCharsets.UTF_8));
            byte[] signed = signature.sign();
            return Base64.getEncoder().encodeToString(signed);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
                | SignatureException e) {
            throw new RuntimeException("RSA签名失败", e);
        }
    }

    /**
     * RSA公钥验签.
     *
     * @param text 原文
     * @param sign 签名
     * @param publicKey 公钥
     * @return 验签结果
     */
    public static boolean rsaVerify(final String text, final String sign, final String publicKey) {
        if (text == null || sign == null || publicKey == null) {
            return false;
        }
        try {
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
            PublicKey key = keyFactory.generatePublic(keySpec);
            Signature signature = Signature.getInstance(RSA_SIGNATURE_ALGORITHM);
            signature.initVerify(key);
            signature.update(text.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException
                | SignatureException e) {
            throw new RuntimeException("RSA验签失败", e);
        }
    }

    /**
     * 字节数组转十六进制字符串.
     *
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(final byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte byteValue : bytes) {
            sb.append(String.format("%02x", byteValue));
        }
        return sb.toString();
    }
} 
